﻿using System;
using System.Web.ModelBinding;

namespace Morus.Presentation
{
    public sealed class BooleanModelBinder : IModelBinder
    {
        public bool BindModel(ModelBindingExecutionContext modelBindingExecutionContext, ModelBindingContext bindingContext)
        {
            Check.Argument.IsNotNull(modelBindingExecutionContext, "modelBindingExecutionContext");
            Check.Argument.IsNotNull(bindingContext, "bindingContext");

            ValueProviderResult value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (value == null)
            {
                return false;
            }

            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);

            bool? model;
            try
            {
                model = GetBooleanValue(value.AttemptedValue);
            }
            catch (Exception ex)
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex);
                return false;
            }

            bindingContext.Model = IsNullableType(bindingContext.ModelType) ? model : model.GetValueOrDefault();

            return true;
        }

        private bool? GetBooleanValue(string value)
        {
            if (value.IsEmpty())
            {
                return null;
            }

            switch (value.Trim().ToLower())
            {
                case "false":
                case "0":
                case "off":
                case "":
                    return false;
                case "true":
                case "1":
                case "on":
                    return true;
                default:
                    return null;
            }
        }

        private static bool IsNullableType(Type type)
        {
            return (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)));
        }

        private static bool IsFormatException(Exception ex)
        {
            while (ex != null)
            {
                if (ex is FormatException)
                {
                    return true;
                }
                ex = ex.InnerException;
            }
            return false;
        }
    }
}
